__author__ = 'Amine'

import json
from jsonschema import Draft4Validator
import pprint
import collections

from print_nice import print_nice


class Model(object):
    def __init__(self):
        self.name = None
        self.schemamod = json.loads(open('model.schema').read())
        self.schemalib = json.loads(open('library.schema').read())
        self.maindict = {}
        self.libdict = {}
        self.abstract_dict ={}

    def feed(self, path):
        try:

            loadmodel = json.loads(open(path).read())

        except (IOError, ValueError) as e:
            if type(e) is IOError:
                #IO error if file or directory doesn't exist
                print e
            else:
                #json syntax parsing error
                print "ERROR in Parsing Model  %s \nCHECK the json syntax of the Model !!" % e
            return

        #-------------Validate a Model Json with the Json Schema "model.schema"
        v = Draft4Validator(self.schemamod)
        errors = sorted(v.iter_errors(loadmodel), key=lambda e: e.path)
        # print the path and the related error
        for error in errors:
            print " ERROR IN JSON MODEL  :  %s -----> :%s" % (list(error.path), error.message)
        #-------------
        self.name = path
        self.maindict = loadmodel
        self.feedlib(self.maindict[u'library'])

    def feedlib(self, path):
        if path is None :
            return
        try:
            loadlib = json.loads(open(path).read())
        except IOError:
            print "Library %s : No such file" % path
            return

        #-------------Validate a Library Json with the Json Schema "library.schema"
        vlib = Draft4Validator(self.schemalib)
        errors = sorted(vlib.iter_errors(loadlib), key=lambda e: e.path)
        # print the path and the related error
        for error in errors:
            print " ERROR IN JSON LIBRARY  :  %s -----> :%s" % (list(error.path), error.message)
        #-------------

        self.libdict = loadlib
        self.runextends(self.maindict,self.libdict)

        #self.print_dict(self.flatten(self.maindict))

        #self.abstract_mod(2, self.maindict, self.abstract_dict) # the user choose the degre of abstraction (here it's 3)

        #self.print_dict(self.abstract_dict)
        #print_nice(self.abstract_dict)


    def runextends(self, dict_main, dict_lib):
        for key, value in dict_main.iteritems():
            if isinstance(value, dict): # If value itself is dictionary
                self.runextends(value, dict_lib)
            elif key == 'extends':
                if value is not None and (dict_main['objects'] is not None or dict_main['relations'] is not None):
                    print('Error in extends %s, extends must be done with a null relations and objects' % value)
                elif value in dict_lib['objects']:
                    dict_main['objects'] = dict_lib['objects'][value]['objects']
                    dict_main['relations'] = dict_lib['objects'][value]['relations']
                    temp = dict_main['properties']
                    dict_main['properties'] = dict_lib['objects'][value]['properties']
                    for k, v in dict_main['properties'].iteritems():
                        if k in temp:
                            dict_main['properties'][k]=temp[k]


    def abstract(self, level, mod_to_abstract, abstract_dict):

       # print " level of abstraction %s " % level
        for key, value in mod_to_abstract.iteritems():
           if isinstance(value, dict): # If value itself is dictionary
              if level > 1:
                  abstract_dict[key] = {}
                  self.abstract(level-1, value, abstract_dict[key])
           else :
               abstract_dict[key] = value

        return abstract_dict

    def flatten(self, d, parent_key=''):
        items = []
        for k, v in d.items():
            new_key = parent_key + '_' + k if parent_key else k
            if isinstance(v, collections.MutableMapping):
                items.extend(self.flatten(v, new_key).items())
            else:
                items.append((new_key, v))
        return dict(items)


    def print_dict(self, mod):

        printer = pprint.PrettyPrinter()
        printer.format = self.my_safe_repr
        printer.pprint(mod)


    # print with prettyprint without u for unicode
    def my_safe_repr(self, object, context, maxlevels, level):
        typ = pprint._type(object)
        if typ is unicode:
          object = str(object)
        return pprint._safe_repr(object, context, maxlevels, level)


